package com.nulldev.util.web.HttpClient.legacy;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import com.nulldev.util.io.IOUtils;
import com.nulldev.util.web.URLUtils.Entry;

@Deprecated
public class MultipartUtility {
	private final String boundary;
	private static final String LINE_FEED = "\r\n";
	private HttpURLConnection httpConn;
	private String charset;
	private OutputStream outputStream;
	private PrintWriter writer;
	private boolean isURLForm;

	/**
	 * This constructor initializes a new HTTP POST request with content type is set
	 * to multipart/form-data
	 * 
	 * @param requestURL
	 * @param charset
	 * @throws IOException
	 */
	public MultipartUtility(final String requestURL, final String charset, final Entry... entries) throws IOException {
		this(requestURL, charset, false, "POST", entries);
	}

	/**
	 * This constructor initializes a new HTTP POST request with content type is set
	 * to multipart/form-data
	 * 
	 * @param requestURL
	 * @param charset
	 * @throws IOException
	 */
	public MultipartUtility(final String requestURL, final String charset, final boolean isURLForm, final String method, final Entry... entries)
			throws IOException {
		this(requestURL, charset, false, "POST", Proxy.NO_PROXY, entries);
	}

	/**
	 * This constructor initializes a new HTTP POST request with content type is set
	 * to multipart/form-data
	 * 
	 * @param requestURL
	 * @param charset
	 * @throws IOException
	 */
	public MultipartUtility(final String requestURL, final String charset, final boolean isURLForm, final String method, final Proxy proxy,
			final Entry... entries) throws IOException {
		this.isURLForm = isURLForm;
		this.charset = charset;

		// creates a unique boundary based on time stamp
		this.boundary = "===" + System.currentTimeMillis() + "===";

		URL url = new URL(requestURL);
		this.httpConn = (HttpURLConnection) url.openConnection(proxy);
		this.httpConn.setUseCaches(false);
		this.httpConn.setDoOutput(true); // indicates POST method
		this.httpConn.setDoInput(true);
		this.httpConn.setRequestMethod(method);
		for (final Entry e : entries) {
			if (e == null || !e.isValid()) {
				continue;
			}
			this.httpConn.setRequestProperty(e.getKey(), e.getValue());
		}

		if (isURLForm) {
			this.httpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
		} else {
			this.httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + this.boundary);
		}
		this.httpConn.setRequestProperty("Accept-Encoding", "gzip, deflate");
		this.outputStream = httpConn.getOutputStream();
		this.writer = new PrintWriter(new OutputStreamWriter(this.outputStream, charset), true);
	}

	private boolean __h_writeOnce;
	private boolean __h_correctOnce;

	/**
	 * Adds a form field to the request
	 * 
	 * @param name  field name
	 * @param value field value
	 */
	public void addFormField(final String name, final String value) {
		if (this.isURLForm) {
			if (this.__h_writeOnce && !this.__h_correctOnce) {
				this.writer.append("&");
				this.__h_correctOnce = true;
			}
			this.writer.append(name + "=" + value + (this.__h_writeOnce ? "&" : ""));
			if (!this.__h_writeOnce) {
				this.__h_writeOnce = true;
			}
		} else {
			this.writer.append("--" + boundary).append(LINE_FEED);
			this.writer.append("Content-Disposition: form-data; name=\"" + name + "\"").append(LINE_FEED);
			this.writer.append("Content-Type: text/plain; charset=" + charset).append(LINE_FEED);
			this.writer.append(LINE_FEED);
			this.writer.append(value).append(LINE_FEED);
		}
		this.writer.flush();
	}

	/**
	 * Adds a upload file section to the request
	 * 
	 * @param fieldName  name attribute in <input type="file" name="..." />
	 * @param uploadFile a File to be uploaded
	 * @throws IOException
	 */
	public void addFilePart(final String fieldName, final File uploadFile) throws IOException {
		String fileName = uploadFile.getName();
		writer.append("--" + boundary).append(LINE_FEED);
		writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(LINE_FEED);
		writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(fileName)).append(LINE_FEED);
		writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
		writer.append(LINE_FEED);
		writer.flush();

		FileInputStream inputStream = new FileInputStream(uploadFile);
		byte[] buffer = new byte[4096];
		int bytesRead = -1;
		while ((bytesRead = inputStream.read(buffer)) != -1) {
			outputStream.write(buffer, 0, bytesRead);
		}
		outputStream.flush();
		inputStream.close();

		writer.append(LINE_FEED);
		writer.flush();
	}

	/**
	 * Adds a header field to the request.
	 * 
	 * @param name  - name of the header field
	 * @param value - value of the header field
	 */
	public void addHeaderField(String name, String value) {
		writer.append(name + ": " + value).append(LINE_FEED);
		writer.flush();
	}

	public void write(final byte[] data) throws IOException {
		this.outputStream.write(data);
		this.outputStream.flush();
	}

	private List<String> respCookies = null;

	/**
	 * Completes the request and receives response from the server.
	 * 
	 * @return a list of Strings as response in case the server returned status OK,
	 *         otherwise an exception is thrown.
	 * @throws IOException
	 */
	public byte[] finish() throws IOException {
		if (!this.isURLForm) {
			writer.append(LINE_FEED).flush();
			writer.append("--" + boundary + "--").append(LINE_FEED);
		}
		writer.close();

		// checks server's status code first
		int status = this.httpConn.getResponseCode();
		if (status == HttpURLConnection.HTTP_OK) {
			this.respCookies = this.httpConn.getHeaderFields().get("Set-Cookie");
			final String encoding = this.httpConn.getContentEncoding();
			InputStream output;
			if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
				output = new GZIPInputStream(this.httpConn.getInputStream());
			} else if (encoding != null && encoding.equalsIgnoreCase("deflate")) {
				output = new InflaterInputStream(this.httpConn.getInputStream(), new Inflater(true));
			} else {
				output = this.httpConn.getInputStream();
			}
			final byte[] data = IOUtils.toByteArray(output);
			httpConn.disconnect();
			return data;
		} else {
			throw new IOException("Server returned non-OK status: " + status);
		}
	}

	public List<String> cookies() {
		return this.respCookies;
	}
}